﻿using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

using iStudio.Structure.Control.Audio.Editor;
using iStudio.Structure.Wave;

namespace iStudio.Editor.BasicRenderer
{
    public class BasicCachedChunk : EditorCachedChunk
    {
        // Background rectangle
        private readonly Rectangle _background;

        // Chunk waveform lines
        private readonly Polygon _leftWaveformPolyLine;
        private readonly Polygon _rightWaveformPolyLine;

        public BasicCachedChunk (WaveTrack track, WaveChunk chunk, EditorCore editorCore) : base (track, chunk, editorCore)
        {
            //
            // Background
            //

            _background = new Rectangle
            {
                Fill = new SolidColorBrush(Color.FromRgb(40, 91, 142)),
                Stroke = new SolidColorBrush(Color.FromRgb(10, 61, 112)),
                StrokeThickness = 1,
                Width = EditorCore.PointThickness * (WaveformData.Length + 2),
                Height = 40,
                RadiusX = 6,
                RadiusY = 6
            };

            //
            // Data
            //

            _leftWaveformPolyLine = new Polygon();
            _rightWaveformPolyLine = new Polygon();

            _leftWaveformPolyLine.Fill = new LinearGradientBrush(Color.FromRgb(49, 113, 177), Color.FromRgb(92, 162, 213), new Point(1, 0), new Point(1, 1));
            _rightWaveformPolyLine.Fill = new LinearGradientBrush(Color.FromRgb(110, 187, 231), Color.FromRgb(191, 226, 248), new Point(1, 0), new Point(1, 1));

            //
            // Fill polylines with data
            //

            _leftWaveformPolyLine.Points.Add (new Point(0, 20));
            _rightWaveformPolyLine.Points.Add (new Point(0, 20));

            for (int i = 0; i < WaveformData.Length; i++)
            {
                _leftWaveformPolyLine.Points.Add(new Point( (i + 1) * EditorCore.PointThickness, (1 - (WaveformData[i] * 0.9)) * 20));
                _rightWaveformPolyLine.Points.Add(new Point( (i + 1) * EditorCore.PointThickness, (20 - ((1 - (WaveformData[i] * 0.9)) * 20)) + 20));
            }

            _leftWaveformPolyLine.Points.Add(new Point( (WaveformData.Length + 1) * EditorCore.PointThickness, 20));
            _rightWaveformPolyLine.Points.Add(new Point( (WaveformData.Length + 1) * EditorCore.PointThickness, 20));

            EditorCore.WaveformCanvas.Children.Add(_background);
            EditorCore.WaveformCanvas.Children.Add(_leftWaveformPolyLine);
            EditorCore.WaveformCanvas.Children.Add(_rightWaveformPolyLine);

            Canvas.SetTop (_background, (_background.Height * Chunk.Channel));
            Canvas.SetTop (_leftWaveformPolyLine, (_background.Height * Chunk.Channel));
            Canvas.SetTop (_rightWaveformPolyLine, (_background.Height * Chunk.Channel));
        }

        protected override void ChunkOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
        {
            //
            // Selection was changed
            //

            if (propertyChangedEventArgs.PropertyName == "IsSelected")
            {
                if (Chunk.IsSelected)
                {
                    _background.Fill = new SolidColorBrush(Color.FromRgb(172, 60, 60));
                    _background.Stroke = new SolidColorBrush(Color.FromRgb(142, 40, 40));

                    _leftWaveformPolyLine.Fill = new SolidColorBrush(Color.FromRgb(232, 120, 120));
                    _rightWaveformPolyLine.Fill = new SolidColorBrush(Color.FromRgb(212, 105, 105));
                }
                else
                {
                    _background.Fill = new SolidColorBrush (Color.FromRgb (40, 91, 142));
                    _background.Stroke = new SolidColorBrush (Color.FromRgb (10, 61, 112));

                    _leftWaveformPolyLine.Fill = new LinearGradientBrush(Color.FromRgb (49, 113, 177), Color.FromRgb (92,162, 213), new Point(1, 0), new Point(1, 1));
                    _rightWaveformPolyLine.Fill = new LinearGradientBrush(Color.FromRgb (110, 187, 231), Color.FromRgb (191, 226, 248), new Point(1, 0), new Point(1, 1));
                }
            }
        }

        protected override void WaveBufferOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
        {
            //
            // Must be here to handle WaveformData change
            //

            base.WaveBufferOnCollectionChanged(sender, notifyCollectionChangedEventArgs);
            
            //
            // Change points in UI too
            //

            if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Replace)
            {
                int index = (int)Math.Floor(notifyCollectionChangedEventArgs.NewStartingIndex / (double)EditorCore.RenderFrameSize);

                //
                // Point is a struct, so it is not transfered by reference
                //

                Point leftPoint = _leftWaveformPolyLine.Points[index + 1];
                Point rightPoint = _rightWaveformPolyLine.Points[index + 1];

                leftPoint.Y = (1 - (WaveformData[index] * 0.9)) * 20;
                rightPoint.Y = (20 - ((1 - (WaveformData[index] * 0.9)) * 20)) + 20;

                _leftWaveformPolyLine.Points[index + 1] = leftPoint;
                _rightWaveformPolyLine.Points[index + 1] = rightPoint;
            }
            else if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Add)
            {
                //
                // Check if we need to add another point here, if we have point mismatch
                //

                if ((_leftWaveformPolyLine.Points.Count - 2) < WaveformData.Length)
                {
                    //
                    // Set last point new position
                    //

                    if (WaveformData.Length > 1)
                    {
                        int index = WaveformData.Length - 2;

                        //
                        // Point is a struct, so it is not transfered by reference
                        //

                        Point leftPoint = _leftWaveformPolyLine.Points[index + 1];
                        Point rightPoint = _rightWaveformPolyLine.Points[index + 1];

                        leftPoint.Y = (1 - (WaveformData[index] * 0.9)) * 20;
                        rightPoint.Y = (20 - ((1 - (WaveformData[index] * 0.9)) * 20)) + 20;

                        _leftWaveformPolyLine.Points[index + 1] = leftPoint;
                        _rightWaveformPolyLine.Points[index + 1] = rightPoint;
                    }

                    // Points in polylines
                    int count = (_leftWaveformPolyLine.Points.Count - 2);

                    // Missing points are in waveform data
                    int missing = WaveformData.Length - count;

                    //
                    // Fix last point - prevent bowtie polygon
                    //

                    Point lastLeftPoint = _leftWaveformPolyLine.Points[_leftWaveformPolyLine.Points.Count - 1];
                    Point lastRightPoint = _rightWaveformPolyLine.Points[_rightWaveformPolyLine.Points.Count - 1];
                    
                    lastLeftPoint.X = (WaveformData.Length + 1) * EditorCore.PointThickness;
                    lastRightPoint.X = (WaveformData.Length + 1) * EditorCore.PointThickness;

                    _leftWaveformPolyLine.Points[_leftWaveformPolyLine.Points.Count - 1] = lastLeftPoint;
                    _rightWaveformPolyLine.Points[_rightWaveformPolyLine.Points.Count - 1] = lastRightPoint;

                    //
                    // Fill in the missing points
                    //

                    for (int i = 0; i < missing; i++)
                    {
                        //
                        // Create new point
                        //

                        Point leftPoint = new Point ((count + i) * EditorCore.PointThickness + EditorCore.PointThickness, 20);
                        Point rightPoint = new Point ((count + i) * EditorCore.PointThickness + EditorCore.PointThickness, 20);

                        _leftWaveformPolyLine.Points.Insert (count + i + 1, leftPoint);
                        _rightWaveformPolyLine.Points.Insert (count + i + 1, rightPoint);
                    }

                    //
                    // Resize rectangle
                    //

                    _background.Width = EditorCore.PointThickness * (WaveformData.Length + 2);
                }
            }
            else if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Remove)
            {
                //
                // Sync frames with cached waveform data
                //

                if ( (_leftWaveformPolyLine.Points.Count - 2) > WaveformData.Length)
                {
                    int frameIndex = (int)Math.Floor(notifyCollectionChangedEventArgs.OldStartingIndex / (double)EditorCore.RenderFrameSize);

                    _leftWaveformPolyLine.Points.RemoveAt (frameIndex + 1);
                    _rightWaveformPolyLine.Points.RemoveAt (frameIndex + 1);

                    //
                    // Resize rectangle and fix last two points
                    //

                    _background.Width = EditorCore.PointThickness * (WaveformData.Length + 2);

                    //
                    // Fix last point - prevent bowtie polygon
                    //

                    Point lastLeftPoint = _leftWaveformPolyLine.Points[_leftWaveformPolyLine.Points.Count - 1];
                    Point lastRightPoint = _rightWaveformPolyLine.Points[_rightWaveformPolyLine.Points.Count - 1];
                    
                    lastLeftPoint.X = (WaveformData.Length + 1) * EditorCore.PointThickness;
                    lastRightPoint.X = (WaveformData.Length + 1) * EditorCore.PointThickness;

                    _leftWaveformPolyLine.Points[_leftWaveformPolyLine.Points.Count - 1] = lastLeftPoint;
                    _rightWaveformPolyLine.Points[_rightWaveformPolyLine.Points.Count - 1] = lastRightPoint;
                }
            }
        }
        public override void SetCanvasProperty(EditorCanvasProperty property, double value)
        {
            //
            // Modify canvas property top, avoid placing tracks on top of one another
            //

            if (property == EditorCanvasProperty.MarginTop)
            {
                Canvas.SetTop (_background, value + (Chunk.Channel * 40));
                Canvas.SetTop (_leftWaveformPolyLine, value + (Chunk.Channel * 40));
                Canvas.SetTop(_rightWaveformPolyLine, value + (Chunk.Channel * 40));
            }
        }
    }
}
